package pCloud.acesscontrol;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import pCloud.PCloudAttributeAndType;
import pCloud.message.DatabaseReply;
import pCloud.message.ReplyMessage;
import pCloud.obligation.ApproximationObligation;
import pCloud.obligation.DataProcessingObligation;
import pCloud.obligation.SlidingWindowObligation;
import pCloud.server.DatabaseHandle;

import com.sun.xacml.Indenter;
import com.sun.xacml.Obligation;
import com.sun.xacml.ParsingException;
import com.sun.xacml.attr.AttributeValue;
import com.sun.xacml.attr.IntegerAttribute;
import com.sun.xacml.attr.StringAttribute;
import com.sun.xacml.ctx.Attribute;
import com.sun.xacml.ctx.RequestCtx;
import com.sun.xacml.ctx.ResponseCtx;
import com.sun.xacml.ctx.Result;

/**
 * PEP for PCloud. It first gets the decision from the PDP, then performs the
 * obligations.
 * 
 */
public class PCloudPEP {

	PCloudPDP pdp;

	public PCloudPEP(PCloudPDP pdp) {
		this.pdp = pdp;
	}

	public Object evaluateRequest(int requestId, String keyColumn, String requestFile,
			DatabaseHandle databaseHandler) throws Exception {
		pdp.setRequestCtx(requestFile);

		RequestCtx requestCtx = pdp.getRequestContext();

		ResponseCtx outputs = pdp.evaluate();

		Set results = outputs.getResults();

		java.util.Iterator it = results.iterator();
		Result result = null;
		int decision = Result.DECISION_NOT_APPLICABLE;

		ReplyMessage replyMessage=null;

		// Assume that there's only one result
		while (it.hasNext()) {
			result = (Result) it.next();
			decision = result.getDecision();
			if (decision != Result.DECISION_PERMIT) {
				replyMessage = new ReplyMessage(requestId, decision, null);
			}
		}
		
		if (decision == Result.DECISION_PERMIT) {
			// 1. Build an obligation map
			HashMap<URI, Obligation> obligationMap = this
					.getObligations(result);

			if (obligationMap.containsKey(new URI(
					PCloudAttributeAndType.OBLIGATION_APPROXIMATION_PARAM))
					|| obligationMap
							.containsKey(new URI(
									PCloudAttributeAndType.OBLIGATION_APPROXIMATION_VALUE))) {
				ApproximationObligation ao = new ApproximationObligation();
				replyMessage = new ReplyMessage(requestId, decision,
						(DatabaseReply) ao.processObligation(keyColumn, databaseHandler,
								obligationMap, requestCtx));
			} else if (obligationMap.containsKey(new URI(
					PCloudAttributeAndType.OBLIGATION_SLIDING_WINDOW_ID))) {
				SlidingWindowObligation swo = new SlidingWindowObligation();
				replyMessage = new ReplyMessage(requestId, decision,
						(DatabaseReply) swo.processObligation(keyColumn, databaseHandler,
								obligationMap, requestCtx));
			} else {
				// 2. For now, assume there's only simple data processing
				// obligation
				DataProcessingObligation dpo = new DataProcessingObligation();
				replyMessage = new ReplyMessage(requestId, decision,
						(DatabaseReply) dpo.processObligation(keyColumn, databaseHandler,
								obligationMap, requestCtx));
			}
		}
		
		replyMessage.setMatchingPolicyIds(result.getMatchingPolicyIds());
		return replyMessage;
	}

	private HashMap<URI, Obligation> getObligations(Result result) {
		HashMap<URI, Obligation> obligationMap = new HashMap<URI, Obligation>();
		Iterator it = result.getObligations().iterator();
		Obligation ob;
		while (it.hasNext()) {
			ob = (Obligation) it.next();
			obligationMap.put(ob.getId(), ob);
		}
		return obligationMap;
	}

	protected String getDecision(int dec) {
		String res;
		if (dec == Result.DECISION_DENY)
			res = "Deny";
		else if (dec == Result.DECISION_INDETERMINATE)
			res = "Intermediate";
		else if (dec == Result.DECISION_NOT_APPLICABLE)
			res = "Not Applicable";
		else
			res = "Permit";
		return res;
	}

}
